#include"server.hpp"
#include<memory>
#include<fstream>
#include<unordered_map>
#include<signal.h>
#include"onlienUser.hpp"


using namespace Server;

// const string dictTxt="./dict.txt";//字典的文件名，配置文件的路径
// unordered_map<string,string> dict;//建立单词映射关系，用于查找字典中需要的意思，通过first找second


static void Usage(string proc)
{  cout<<"\nUsage:\n\t"<<proc<<" local_port\n\n";//类已经定义了ip因此只需要定义端口号

}
// static bool cutString(const string &target,string *s1,string *s2,const string &sep)//获得单词和意思放入map中
// {
//    auto pos=target.find(sep);
//    if(pos==string::npos)return false;
//    *s1=target.substr(0,pos);//左闭右开
//    *s2=target.substr(pos+sep.size());
//    return true;
// }
// static void initDict()//c++的IO操作，建立一个词库
// {
//     ifstream in(dictTxt,std::ios::binary);//打开文件进行读取，以二进制的模式binary
//     if(!in.is_open())
//     {
//         cerr << "open file " << dictTxt << " error" << endl;
//        exit(OPEN_ERR);

//     }
//     string line;
//     string key,value;
//     while(getline(in,line))//从文件流中读到字符串内
//     {
//      if(cutString(line,&key,&value,":"))//分离单词和意思
//      {
//         dict.insert(make_pair(key,value));
//      }

//     }
//     in.close();
//     cout<<"load dict success"<<endl;

// }
// void reload(int signo)
// {
//     (void)signo;
//     initDict();
// }
// static void debugPrint()
// {  
//     for(auto &dt:dict)
//     {
//         cout<<dt.first<<" # "<<dt.second<<endl;
//     }
      
// }


// //map实现单词翻译，目的客户端发中文，服务端发英文
// //先创建映射关系，然后建立词典，打开词典，获得文件描述符，通过文件描述符传文件内容按行获取getline到进程字符串中，字符串截取，然后给到映射关系存储的map
// void handlerMessage(int sockfd,string clientip, uint16_t clientport, string message)//收到客户端信号，进行回调函数
// {
//    //可以对message进行特定的业务处理，而不关心message怎么来的 ---- server通信和业务逻辑解耦！
//    string response_message;
//    auto iter=dict.find(message);//寻找字典的分隔符
//    if(iter==dict.end())//说明没有找到
//    { response_message="unknown";}
//    else
//    {response_message =iter->second;}//找到了单词的意思
   
//    //然后向客户端发单词的意思
//    struct sockaddr_in client;
//    bzero(&client,sizeof(client));
//    client.sin_family=AF_INET;
//    client.sin_port=htons(clientport);
//    client.sin_addr.s_addr=inet_addr(clientip.c_str());//字符串转整型
//    sendto(sockfd,response_message.c_str(),response_message.size(),0,(struct sockaddr*)&client,sizeof(client));
//    //sockfd的作用是告诉客户端，谁发的消息，为了发回到服务端
// }


// //发命令字符串，目的实现客户端对服务端的命令shell操作后，对客户端的结果返回
// void execCommand(int sockfd,string clientip,uint16_t clientport,string cmd)
// { 
//     string response;
//     FILE*fp=popen(cmd.c_str(),"r");//pipe+fork+exec*  第一个参数是命令，执行完毕得到结果，通过管道，将结果以文件的方式返回。
//    if(fp==nullptr)response=cmd+"exec failed";
//    char line[1024];
//    while(fgets(line,sizeof(line),fp))//从管道中的缓冲区读取字符串到line中
//    {
//     response+=line;
//    }
//    pclose(fp);
//    struct sockaddr_in client;
//    memset(&client,0,sizeof(client));//初始化
//    client.sin_family=AF_INET;
//    client.sin_port=htons(clientport);
//    client.sin_addr.s_addr=inet_addr(clientip.c_str());
//    sendto(sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&client,sizeof(client));




// }
OnlineUser onlineuser;//定义类对象
//demo3
void  routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
   if(message=="online") onlineuser.addUser(clientip,clientport);
   if(message=="offline") onlineuser.delUser(clientip,clientport);
   if(onlineuser.isOnline(clientip,clientport))
   {
    onlineuser.broadcastMessage(sockfd,clientip,clientport,message);
   }
   else
   {
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    string response = "你还没有上线，请先上线，运行: online";

    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
   }


}
//回调方法，功能：做用户管理，需要一个封装好的用户管理的类定义的对象，添加客户ip和端口号，判断用户在线，就进行消息转发（路由），没有在线，服务端就给客户端发个消息
//服务器进程要知道 ip port
int main(int argc,char *argv[])
{  
    if(argc!=2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port=atoi(argv[1]);//获取外部输入的端口号
   // signal(2,reload);//信号替换
    //initDict();
    //debugPrint();
    unique_ptr<udpServer> usvr(new udpServer(routeMessage,port));
    usvr->initServer();//创建
    usvr->start();//接收


}
